﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;

namespace WindowsWorkflow.SequentialActivities
{
	public partial class Graph: CompositeActivity
	{
		#region fields

		private Dictionary<string, bool> transitionStatus;
		private bool exiting;
		#endregion

		#region props

		public static DependencyProperty ArcsProperty =
			DependencyProperty.Register(
				"Arcs", typeof (List<Arc>), typeof (Graph),
				new PropertyMetadata(
					DependencyPropertyOptions.Metadata |
					DependencyPropertyOptions.ReadOnly,
					new Attribute[]
						{
							new DesignerSerializationVisibilityAttribute
								(DesignerSerializationVisibility
								 	.Content)
						}));

		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public List<Arc> Arcs
		{
			get
			{
				return ((List<Arc>)(base.GetValue(Graph.ArcsProperty)));
			}
			//set
			//{
			//    base.SetValue(Graph.ArcsProperty, value);
			//}
		}

		public static DependencyProperty IsEntryProperty = 
			DependencyProperty.RegisterAttached("IsEntry", typeof(bool), typeof(Graph),
			new PropertyMetadata(DependencyPropertyOptions.Metadata));

		public static object GetIsEntry(object dependencyObject)
		{
			DependencyObject o = dependencyObject as DependencyObject;
			return o.GetValue(Graph.IsEntryProperty);
		}

		public static void SetIsEntry(object dependencyObject, object value)
		{
			DependencyObject o = dependencyObject as DependencyObject;
			o.SetValue(Graph.IsEntryProperty, value);
		}

		public static DependencyProperty IsExitProperty =
					DependencyProperty.RegisterAttached("IsExit", typeof(bool), typeof(Graph),
					new PropertyMetadata(DependencyPropertyOptions.Metadata));

		public static object GetIsExit(object dependencyObject)
		{
			DependencyObject o = dependencyObject as DependencyObject;
			return o.GetValue(Graph.IsExitProperty);
		}

		public static void SetIsExit(object dependencyObject, object value)
		{
			DependencyObject o = dependencyObject as DependencyObject;
			o.SetValue(Graph.IsExitProperty, value);
		}
		#endregion

		public Graph():base()
		{
			base.SetReadOnlyPropertyValue(Graph.ArcsProperty,new List<Arc>());
		}

		protected override void Initialize(IServiceProvider provider)
		{
			exiting = false;
			transitionStatus=new Dictionary<string, bool>();
			foreach(Arc arc in this.Arcs)
			{
				transitionStatus.Add(arc.name, false);
			}
			base.Initialize(provider);
		}

		protected override void Uninitialize(IServiceProvider provider)
		{
			transitionStatus = null;
			base.Uninitialize(provider);
		}

		protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
			if (EnabledActivities.Count == 0)
				return ActivityExecutionStatus.Closed;

			foreach(Activity child in EnabledActivities)
			{
				bool entry = (bool) Graph.GetIsEntry(child);
				if(entry)
				{
					Run(executionContext, child);
					break;
				}
			}
			return ActivityExecutionStatus.Executing;
		}

		private void Run(ActivityExecutionContext context, Activity child)
		{
			ActivityExecutionContextManager manager = context.ExecutionContextManager;
			ActivityExecutionContext c = manager.CreateExecutionContext(child);
			c.Activity.Closed += new EventHandler<ActivityExecutionStatusChangedEventArgs>(Activity_Closed);
			c.ExecuteActivity(c.Activity);
		}

		void Activity_Closed(object sender, ActivityExecutionStatusChangedEventArgs e)
		{
			e.Activity.Closed -= Activity_Closed;

			ActivityExecutionContext context = sender as ActivityExecutionContext;
			// get the name before completing the AEC
			string completedChildName = e.Activity.Name;
			bool exitNow = (bool) Graph.GetIsExit(e.Activity);

			ActivityExecutionContextManager manager = context.ExecutionContextManager;
			ActivityExecutionContext c = manager.GetExecutionContext(e.Activity);
			manager.CompleteExecutionContext(c,false);

			if(exiting || exitNow)
			{
				// no executing child activities
				if(manager.ExecutionContexts.Count==0)
					context.CloseActivity();
				else if (exitNow) // just completed an exit activity
				{
					exiting = true;
					foreach(ActivityExecutionContext ctx in manager.ExecutionContexts)
					{
						if(ctx.Activity.ExecutionStatus == ActivityExecutionStatus.Executing)
							ctx.CancelActivity(ctx.Activity);
					}
				}
				else
				{
					// mark all outgoing transitions as true 
					foreach(Arc arc in this.Arcs)
					{
						if(arc.FromActivity.Equals(completedChildName))
							this.transitionStatus[arc.name] = true;
					}

					foreach(Activity child in EnabledActivities)
					{
						bool entry = (bool) Graph.GetIsEntry(child);
						if(!entry)
						{
							// a child activity can run only if all incoming transitions are true
							// and it is not the entry activity 
							bool canRun = true;
							foreach(Arc arc in this.Arcs)
							{
								if(arc.ToActivity.Equals(child.Name))
									if(transitionStatus[arc.name]== false)
										canRun = false;
							}
							if(canRun)
							{
								// when we run a child activity, mark its incoming transitions as false
								foreach(Arc arc in this.Arcs)
								{
									if(arc.ToActivity.Equals(child.Name))
										transitionStatus[arc.name] = false;
								}
								Run(context, child);
							}
						}
					}
				}
			}
		}
	}
}
